
#include <string.h>
#include "hardware.h"
#include "sys.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "debug.h"
#include "fout.h"
#include "USART.h"
#include "cmd.h"
#include "modem.h"
#include "status.h"
#include "macros.h"
#include "s_scanf.h"

typedef struct
{
	unsigned in_session				: 1;
	unsigned session_finish			: 1;
	unsigned online 					: 1;
	unsigned socket_opened 			: 1;
	unsigned socket_broken 			: 1;
	unsigned srv_channel_opened 	: 1;
	unsigned srv_session_fail	 	: 1;
}net_status_t;

net_status_t net_status;

#define   MODEM_NO_NEED__OK								0
#define   MODEM_WAIT_FOR__OK								1
#define   MODEM_ANSWER__ERROR								1	
#define   MODEM_ERROR__MODEM_ANSWER_TIMEOUT			-2
#define   ERROR__CANT_GET_IP								-3
#define   ERROR__CANT_OPEN_SOCKET						-4

xQueueHandle modem_out_queue;	  /* modem send data to this queue */
xQueueHandle modem_queue;		  /* modem data after filter of modem async. message */
xQueueHandle modem_cmd_queue;	  /* send command to modem network task; target: do connect to server and send message */

char modem_rec[U2_REC_BUF_SIZE];

#define   MODEM_MSG_BUFFER_SIZE		(U2_REC_BUF_SIZE*2)

char modem_msg_buffer[MODEM_MSG_BUFFER_SIZE];

char modem_imei[16] = "xxxxxxxxxxxxxxx";

#define   ENCRYPT_KEY_SIZE		8
#define   ENCRYPT_KEY_MASK		0x07

unsigned char encryptkey_receive[ENCRYPT_KEY_SIZE];
int pEncryptKey_Receive;

unsigned char encryptkey_transmit[ENCRYPT_KEY_SIZE];
int pEncryptKey_Transmit;

void net_status_reset_flags ( void )
{
	memset(&net_status,0,sizeof(net_status));
}

/* example: integer 0xD207FC59 to string "59FC07D2" */
unsigned char* digit_to_hex( unsigned char* Char_Dist, unsigned char* Hex, unsigned int HexCount ) /* HexToChar */
{
	unsigned char temp, *Char;

	Char = Char_Dist;
	
	while ( HexCount-- )
	{ 
		temp = *Hex >> 4;
		if ( temp >= 10 )
			temp = temp + 'A' -10;  
		else
			temp += '0';

		*Char++ = temp;  
	
		temp = *Hex & 0x0F;
		if ( temp >= 10 )
			temp = temp + 'A' -10;
		else
			temp += '0';

		*Char++ = temp;

		Hex++;
	}

	*Char = 0;
	return Char_Dist;  
} 



/* example: string: "FA05E823" to integer 0x23E805FA*/
/* return count of gigits bytes */
int hex_to_digit( char* Hex_Dist, char* Char ) /* CharToHex */
{
	char temp, *Hex;

	Hex = Hex_Dist;

	unsigned char bytes_cnt = 0;
	
	while ( (*Char) >= (' ') )
	{
		temp = *Char++;
			if ( temp >= 'a' ) 
		{
			temp = (temp - 'a') + 10;
		}
		else
			if ( temp >= 'A' )
			{
				temp = (temp - 'A') + 10;
			}
		else
			temp -= '0';

		*Hex = temp<<4;

		temp = *Char++;
		if ( temp >= 'a' ) 
		{
			temp = (temp - 'a') + 10;
		}
		else
			if ( temp >= 'A' )
			{
				temp = (temp - 'A') + 10;
			}
		else
			temp -= '0';
		
	   *Hex |= temp;

		bytes_cnt++;
		Hex++;
	}

	return bytes_cnt; 
}


static unsigned long getCRC(unsigned long Init, unsigned long Poly, unsigned char* dataBuf, int lenBuf)
{
	unsigned long k, n;
	unsigned char i;

	while( lenBuf-- )
	{
      n = ( (Init>>24) ^ *dataBuf++ ) & 0xFFL;

      k = n<<24;

      for( i = 0; i < 8; i++ )
		{
			if( k & 0x80000000 )
				k = ( k<<1 ) ^ Poly;
			else
				k <<= 1;
		}
	
      Init = k ^ ( Init<<8 );
	}

	return Init;
}


static void getKeys(unsigned char* recvCryptKey, unsigned char* sendCryptKey, unsigned char* RandKey )
{
	unsigned long Init, CRC1, CRC2;

	memcpy(&Init, RandKey, 4);

	CRC1=getCRC(Init, 0x6C0817A1, RandKey, 8);
	CRC2=getCRC(CRC1, 0x6042B843, RandKey, 8);

//	dbg(" **1** CRC1: %d",(int)CRC1); //Dima

/* 	CRC1=GetCRC(Init, 0x2936D371, RandKey, 8); */
/* 	CRC2=GetCRC(CRC1, 0x6C0817A1, RandKey, 8); */

	memcpy(recvCryptKey, (unsigned char*)&CRC1, 4);

	memcpy(&recvCryptKey[4], (unsigned char*)&CRC2, 4);

 
	memcpy(&Init, &RandKey[4], 4);

	CRC1=getCRC(Init, 0x2936D371, RandKey, 8);
	CRC2=getCRC(CRC1, 0xA9E349E5, RandKey, 8);
	
//	dbg(" **2** CRC1: %d",(int)CRC1); //Dima

/* 	CRC1=GetCRC(Init, 0xA9E349E5, RandKey, 8); */
/* 	CRC2=GetCRC(CRC1, 0x6042B843, RandKey, 8); */

	memcpy(sendCryptKey, (unsigned char*)&CRC1, 4);

	memcpy(&sendCryptKey[4], (unsigned char*)&CRC2, 4);
}

#define   NET_BUF_SIZE				64
#define   NET_BUF_HEX_SIZE			(NET_BUF_SIZE*2)

char net_buf[NET_BUF_SIZE];
char net_buf_hex[NET_BUF_HEX_SIZE];

int net_srv_send_data ( char *data, unsigned char data_size)
{
	if ( data == NULL )
		return -1;
	
	unsigned char i;

	for ( i = 0; i < data_size; i++ )
	{
		data[i] ^= encryptkey_transmit[pEncryptKey_Transmit++ & ENCRYPT_KEY_MASK];
	}

	digit_to_hex(net_buf_hex, data, data_size);
	

	vTaskDelay(500 MSEC);
	/* send data to modem accumulated buffer */
	fout(FOUT_DEV_U2,"AT+MIPSEND=1,\"%s\"\r",net_buf_hex);

	
	if ( modem_read_msg(modem_msg_buffer,30000,MODEM_WAIT_FOR__OK) == 0 )
	{
		vTaskDelay(500 MSEC);
		/* push data from accumulated buffer */
		fout(FOUT_DEV_U2, "AT+MIPPUSH=1\r");
		

		if ( modem_read_msg(modem_msg_buffer,30000,MODEM_WAIT_FOR__OK) == 0 )
		{
			dbg("===== data was sent ======"); //Dima
		}
	}

	return 0;
}

int net_srv_send_cmd ( char *cmd, unsigned char cmd_size)
{
	memcpy(net_buf,cmd,cmd_size);
	
	net_srv_send_data(net_buf,cmd_size);
}

char key[20];
// +MIPRTCP: 1,0,0D512FC0553CEADE
// +MIPRTCP: 1,0,022DB93EF1F5022D
int net_server_handshake ( char *msg )
{
	unsigned char i;
	//char open_key_hex[20]="";
	char *open_key_hex;
	char open_key[10]="";

	
//	s_scanf(msg, "\r\n+MIPRTCP: 1,0,%s\r",&open_key_hex[0]);
	open_key_hex = rindex(msg,',');
	open_key_hex++;

	dbg("open_key_hex: [%s]", open_key_hex); //Dima
	
	hex_to_digit(open_key,open_key_hex);
	
	getKeys(encryptkey_receive, encryptkey_transmit, (unsigned char*)open_key);
	pEncryptKey_Receive = 0; /* Initialization of Key Pointers for this Session */
	pEncryptKey_Transmit = 0;

	/* memset(encryptkey_receive,0,8); */
	/* memset(encryptkey_transmit,0,8); */


	digit_to_hex(key, encryptkey_receive, 8);
	dbg("::: encryptkey_receive: %s", key); //Dima

	digit_to_hex(key, encryptkey_transmit, 8);
	dbg("::: encryptkey_transmit: %s", key); //Dima

	memcpy(net_buf, (char*)encryptkey_receive, ENCRYPT_KEY_SIZE);
	memcpy(&net_buf[ENCRYPT_KEY_SIZE], modem_imei, 16);
	memcpy(&net_buf[ENCRYPT_KEY_SIZE+16], "SS", 2);
	memcpy(&net_buf[ENCRYPT_KEY_SIZE+16+2], (char*)&dev_status, sizeof(dev_status));

	unsigned char data_size = ENCRYPT_KEY_SIZE + 16 + 2 + sizeof(dev_status); //46
	
	net_srv_send_data(net_buf, data_size);
	
	
#if 0
	for ( i = 0; i < data_size; i++ )
	{
		net_buf[i] ^= encryptkey_transmit[pEncryptKey_Transmit++ & ENCRYPT_KEY_MASK];
	}

	digit_to_hex(net_buf_hex, net_buf, data_size);
	

	vTaskDelay(500 MSEC);
	/* send data to modem accumulated buffer */
	//fout(FOUT_DEV_U2,"AT\r");
	fout(FOUT_DEV_U2,"AT+MIPSEND=1,\"%s\"\r",net_buf_hex);

	
	if ( modem_read_msg(modem_msg_buffer,10000,MODEM_WAIT_FOR__OK) == 0 )
	{
		vTaskDelay(500 MSEC);
		/* push data from accumulated buffer */
		fout(FOUT_DEV_U2, "AT+MIPPUSH=1\r");
		

		if ( modem_read_msg(modem_msg_buffer,10000,MODEM_WAIT_FOR__OK) == 0 )
		{
			dbg("===== staus was sent ======"); //Dima
		}
	}
#endif

	return 0;
}


void modem_init ( void )
{
	modem_out_queue = xQueueCreate( 3, sizeof(u2_rec_buf));
	modem_cmd_queue = xQueueCreate( 2, sizeof(modem_net_msg_t));
	modem_queue = xQueueCreate( 3, sizeof(u2_rec_buf));
	
	net_status_reset_flags();
}


const char* modem_cmd_list[] =
{
	"ATE0\r",						  /* echo OFF */
							  /* echo OFF */
//SMS in TEXT mode;extended format reporting.Example instead of "RING" "+CREG: VOICE";
//SMS with indication(1) full indication + message(2)
	"AT+CMGF=1;+CRC=1;+CNMI=,1\r",
//	"AT+CGSN\r",
	"AT+CLIP=1\r",
	"AT+MMICG=3\r",
	"AT+MMICV=2\r",
	//"AT+CLVL=7",					  /* volume 0 - 7 default 4 */
	"AT+CLVL=4\r",					  /* volume 0 - 7 default 4 */
//	"ATS96",
	"AT+CSQ\r",

#if defined(MODEM_TYPE__SIMCOM)
//	"AT+CENG=1",					  /* neighbouring cells */
	/* audio */
	"ATL3",						  /* Volume 0 - 3 */
	//"AT+CLVL=80\r",
	"AT+CLVL=100",
	//"AT+CMIC=0,15\r",
	"AT+CMIC=0,3",
	"AT+CMGDA=\"DEL READ\"",
#else
/* 	"AT+MAFEAT=1,1;+MAFEAT=2,1", */
/* 	"AT+MMICG=24;+MAVOL=1,1,5", */
	"AT+CMGD=1,4\r",
	"AT+CNMI=1,1\r",					  /* sms receive indication (+CMTI: "SM",1) */
#endif

	NULL
};

int _modem_read_msg ( char *answer_buf, unsigned int timeout, unsigned char wait_for_ok )
{
	int res;
	char *p;

	if ( answer_buf == NULL )
		p = modem_msg_buffer;
	else
		p = answer_buf;
	
	*p = 0;

	for(;;)
	{
		/* wait for answer */
		res = xQueueReceive(modem_out_queue,&modem_rec[0], (timeout MSEC));
	
		if ( res == pdFALSE )
		{
			return MODEM_ERROR__MODEM_ANSWER_TIMEOUT; 
		}
	
		strcat(p, modem_rec);

		/* see for modem end of message ("\r\nOK\r\n") or "ERROR"*/
		if ( strstr(p,"ERROR") )
		{
			return MODEM_ANSWER__ERROR;
		}

		if ( wait_for_ok == 0 || strstr(p, "\r\nOK\r\n") )
		{
			return 0;
		}

		p += strlen(modem_rec);
	}
}

int modem_read_msg ( char *answer_buf, unsigned int timeout, unsigned char wait_for_ok )
{
	int res;
	char *p;

	if ( answer_buf == NULL )
		p = modem_msg_buffer;
	else
		p = answer_buf;
	
	*p = 0;

	for(;;)
	{
		/* wait for answer */
		res = xQueueReceive(modem_queue,&modem_rec[0], (timeout MSEC));
	
		if ( res == pdFALSE )
		{
			return MODEM_ERROR__MODEM_ANSWER_TIMEOUT; 
		}
	
		strcat(p, modem_rec);

		/* see for modem end of message ("\r\nOK\r\n") or "ERROR"*/
		if ( strstr(p,"ERROR") )
		{
			return MODEM_ANSWER__ERROR;
		}

		if ( wait_for_ok == 0 || strstr(p, "\r\nOK\r\n") )
		{
			return 0;
		}

		p += strlen(modem_rec);
	}
}

int modem_get_imei ( void )
{
	char *p;
	fout(FOUT_DEV_U2,"AT+CGSN\r");
	
	// +CGSN: "352288031771434" 
	if ( _modem_read_msg(modem_msg_buffer,1000,MODEM_WAIT_FOR__OK) == 0 )
	{	
		if ( (p=strstr(modem_msg_buffer, "+CGSN:")) )
		{
			s_scanf(p, "+CGSN: \"%s\"",&modem_imei[0]);
			return 0;
		}
	}

	return -1;
}

void modem_task ( void *pvParameters )
{
	int res;
	unsigned char mcmd_index, try_cnt;

	PIN_MODEM_POWER = 0;
	PIN_MODEM_POWER_DIR = PIN_OUTPUT;
	PIN_MODEM_POWER = 0;
	
	vTaskDelay(500 MSEC);

	PIN_MODEM_POWER = 1;
	PIN_MODEM_WAKEUP_DIR = PIN_OUTPUT;
	PIN_MODEM_WAKEUP = 0;

	vTaskDelay(3000 MSEC);

	
	for(mcmd_index = 0; modem_cmd_list[mcmd_index] != NULL; mcmd_index++)
	{
		for(try_cnt = 0; try_cnt < 3; try_cnt++)
		{
			dbg("send to modem: %s", modem_cmd_list[mcmd_index]); //Dima
 			fout(FOUT_DEV_U2,modem_cmd_list[mcmd_index]);
			if ( (res=_modem_read_msg(modem_msg_buffer,1000,MODEM_WAIT_FOR__OK)) == 0 )
			{
				dbg("modem_msg_buffer: %s", modem_msg_buffer); //Dima
				break;
			}
			else
			{
				dbg("modem_msg_buffer: %s", modem_msg_buffer); //Dima
				dbg("try........"); //Dima
				vTaskDelay(500 MSEC);
			}
		}

		if ( res == MODEM_ERROR__MODEM_ANSWER_TIMEOUT )
		{
			sys_reset();
		}
	}

	dbg("=== Modem Init. is finished ==="); //Dima

	modem_get_imei();
	dbg("modem_imei: %s", modem_imei); //Dima

	
	for(;;)
	{
		
		xQueueReceive(modem_out_queue,&modem_rec[0],portMAX_DELAY);
		
		dbg("<< %s", modem_rec); //Dima

		if ( strstr(modem_rec,"+MIPSTAT: 1,1") )
		{
			net_status.socket_broken = TRUE;
			net_status.socket_opened = FALSE;
			dbg("========= net_status.socket_broken = TRUE; ============="); //Dima
		}
		else
		{
			xQueueSend(modem_queue,&modem_rec[0],portMAX_DELAY);
		}
		
		memset(modem_rec,0,sizeof(modem_rec));

		portYIELD();
	}
}

int is_ip_addr ( char *ip )
{
	unsigned char cnt = 0;

	dbg("ip: %s", ip); //Dima
	while(*ip)
	{
		if ( *ip == '.' )
			cnt++;

		ip++;
	}

	if ( cnt == 3 )
		return TRUE;

	return FALSE;
}

int modem_net_ip ( unsigned char get_ip )
{
	int res;
	
	dbg(""); //Dima

	if ( get_ip )
	{	
		fout(FOUT_DEV_U2,"AT+MIPCALL=1,\"\"\r");
	}
	else
	{
		fout(FOUT_DEV_U2,"AT+MIPCALL=0\r");
	}
	
	res = modem_read_msg(modem_msg_buffer,4000,MODEM_WAIT_FOR__OK);
//	dbg("1. modem_rec: %s; res: %d", modem_rec,res); //Dima
	
	dbg("."); //Dima

	if ( res != 0 )
	{
		return res;
	}

	/* wait for +MIPCALL: 10.169.5.179 */
	res = modem_read_msg(modem_msg_buffer,10000,MODEM_NO_NEED__OK);
	
	if ( strstr(modem_msg_buffer, "+MIPCALL: ") )
	{
		if ( get_ip && is_ip_addr(modem_msg_buffer))
		{
			net_status.online = TRUE;
		}	
		else
			net_status.online = FALSE;
			
	}
	
	return res;
}

int modem_net_socket ( char *ip_addr, char *port)
{
	if ( ip_addr != NULL )
	{
		fout(FOUT_DEV_U2,"AT+MIPOPEN=1,4528,\"%s\",%s,0\r",ip_addr,port);
	}
	else
	{
		fout(FOUT_DEV_U2,"AT+MIPCLOSE=1\r");
	}
	
	modem_read_msg(modem_msg_buffer,40000,MODEM_WAIT_FOR__OK);

	if ( ip_addr != NULL)
	{
		if ( strstr(modem_msg_buffer, "+MIPOPEN: 1") )
		{
			net_status.socket_opened = TRUE;
			net_status.socket_broken = FALSE;
			return 0;
		}
	}
	else
	{
		if ( strstr(modem_msg_buffer, "+MIPCLOSE: 1") )
		{
			net_status.socket_opened = FALSE;
			return 0;
		}
	}

	return -1;
}

int net_srv_wait_for_open_key(unsigned int timeout)
{
	/* wait for open key  */
	modem_read_msg(modem_msg_buffer,timeout,MODEM_NO_NEED__OK);

	dbg(" [%s]",modem_msg_buffer); //Dima

	if ( strstr(modem_msg_buffer,"+MIPRTCP: 1") )
	{
		net_server_handshake(modem_msg_buffer);
		
		return 0;
	}	

	return -1;
}

int net_data_get ( char *buf_out, char *buf_in )
{
	unsigned char n, i;
	//s_scanf(buf_in, "\r\n+MIPRTCP: 1,0,%s\r",buf_out);
	char *p = rindex(buf_in, ',');
	
	if ( p == NULL )
		return -1;

	p++;
	
	n = hex_to_digit(buf_out,p);

	dbg("n: %d", n); //Dima

	for( i = 0; i < n; i++)
	{
		buf_out[i] ^= encryptkey_receive[(pEncryptKey_Receive++ & ENCRYPT_KEY_MASK)];
	}

	return n;
}

int net_srv_wait_for (char *cmd, unsigned int timeout )
{
	char data[8];
	unsigned char n;

	dbg(""); //Dima

	modem_read_msg(modem_msg_buffer,timeout,MODEM_NO_NEED__OK);

	dbg(" [%s]",modem_msg_buffer); //Dima


	if ( strstr(modem_msg_buffer,"+MIPRTCP: 1") )
	{
		if ( (n=net_data_get(data, modem_msg_buffer)) > 0 )
		{
			data[n] = 0;
			dbg("data: %s",data); //Dima

			if ( strstr(data, cmd) )
			{
				return n;
			}
		}
	}	

	return -1;
}

#define   CONNECTED_TO_SERVER		1

int modem_net_connect_to_server ( char *ip_addr, char *port )
{
	unsigned char try;

	if ( ip_addr )
	{
		for( try = 0; try < 15; try++ ) 
		{
			dbg("try: [%d] for get IP",try); //Dima
			
			modem_net_ip(1);
			if ( net_status.online )
				break;
			vTaskDelay(2000 MSEC);
		}

		if ( ip_addr && net_status.online == FALSE ) 
		{
			return ERROR__CANT_GET_IP;
		}
	
		vTaskDelay(500 MSEC);

		modem_net_socket(ip_addr, port);

		if ( ip_addr && net_status.socket_opened == FALSE ) 
		{
			return ERROR__CANT_OPEN_SOCKET;
		}
	}
	else
	{
		if ( net_status.socket_opened )
		{
			modem_net_socket(ip_addr, port);
			modem_read_msg(modem_msg_buffer,40000,MODEM_NO_NEED__OK);
		
			if ( strstr(modem_msg_buffer, "+MIPCLOSE: 1") )
			{
				dbg("goto offline"); //Dima
			}
			vTaskDelay(500 MSEC);
		}

		modem_net_ip(0);
	}

	return 0;
}

//*3031 netvision; *6900 hot
void net_close_session ( void )
{
	modem_net_connect_to_server(NULL,NULL);
	net_status.in_session = FALSE;
	net_status.session_finish = TRUE;
}

modem_net_msg_t modem_net_msg;

// 79.176.99.88 4567
void modem_net_task ( void *pvParameters )
{
	
	for(;;)
	{
		xQueueReceive(modem_cmd_queue, &modem_net_msg, portMAX_DELAY);
		
		dbg("modem_net_msg.port: %s", modem_net_msg.port); //Dima
		dbg("modem_net_msg.ip_addr: %s", modem_net_msg.ip_addr); //Dima

		net_status_reset_flags();
		
		net_status.in_session = TRUE;
		
		modem_net_connect_to_server(modem_net_msg.ip_addr, modem_net_msg.port);
		if ( net_status.socket_opened )
		{
			dbg("==== socket open ====="); //Dima
			vTaskDelay(1000 MSEC);
		} 
		else
		{
			net_status.srv_session_fail = TRUE;
			goto END_OF_SESSION;
 		}
		
		if ( net_status.socket_broken )
		{
			goto END_OF_SESSION;
		}
		
		if ( net_srv_wait_for_open_key(35000) == 0 )
		{
			dbg("===== handshake: OK ====="); //Dima
		}
		else
			{
				goto END_OF_SESSION;
			}


		if ( net_srv_wait_for("NX",35000) > 0 )
		{
			dbg("===== NX ====="); //Dima
		}
		else
			{
				goto END_OF_SESSION;
			}

		vTaskDelay(500 MSEC);

		if ( net_status.socket_broken )
		{
			goto END_OF_SESSION;
		}
		
		/* send EC (end of command) */
		net_srv_send_cmd("EC",2);

		if ( net_status.socket_broken )
		{
			goto END_OF_SESSION;
		}

		if ( net_srv_wait_for("ES",35000) > 0 )
		{
			dbg("===== ES ====="); //Dima
		}

			
	  END_OF_SESSION:
		if ( net_status.socket_broken )
			net_status.srv_session_fail = TRUE;

		net_close_session();
		
		if ( net_status.srv_session_fail )
		{
			dbg("============= Send status to server: FAIL :-( ==========="); //Dima
		}
		else
		{
			dbg("========= Send status to server: SUCCSESSFUL :-) ========="); //Dima
		}
	}
}
